// import com.alibaba.druid.util.FnvHash;

// import java.util.Arrays;
// import java.util.HashMap;
// import java.util.Map;

#include "Keywords.h"
#include <algorithm>
#include "../../utils/FnvHash.h"
#include "../../utils/log.h"

// class Keywords {
//     public:
//     std::shared_ptr<std::unordered_map<std::string, Token_ptr>> keywords;

//     std::vector<long> hashArray;
//     std::vector<Token_ptr> tokens;

//     static Keywords_ptr DEFAULT_KEYWORDS;
//     static Keywords_ptr SQLITE_KEYWORDS;
//     static Keywords_ptr DM_KEYWORDS;
Keywords_ptr Keywords::DEFAULT_KEYWORDS =  nullptr;
Keywords_ptr Keywords::SQLITE_KEYWORDS =  nullptr;
Keywords_ptr Keywords::DM_KEYWORDS =  nullptr;

bool Keywords::__init = Keywords::init();
bool Keywords::init()
{
  std::shared_ptr<std::unordered_map<std::string, Token_ptr>> map = std::make_shared<std::unordered_map<std::string, Token_ptr>>();
  // Map<String, Token> map = new HashMap<String, Token>();

  map->insert(std::make_pair("ALL", Token_ptr(new Token(&Token::ALL))));
  map->insert(std::make_pair("ALTER", Token_ptr(new Token(&Token::ALTER))));
  map->insert(std::make_pair("AND", Token_ptr(new Token(&Token::AND))));
  map->insert(std::make_pair("ANY", Token_ptr(new Token(&Token::ANY))));
  map->insert(std::make_pair("AS", Token_ptr(new Token(&Token::AS))));
  map->insert(std::make_pair("ENABLE", Token_ptr(new Token(&Token::ENABLE))));
  map->insert(std::make_pair("DISABLE", Token_ptr(new Token(&Token::DISABLE))));
  map->insert(std::make_pair("ASC", Token_ptr(new Token(&Token::ASC))));
  map->insert(std::make_pair("BETWEEN", Token_ptr(new Token(&Token::BETWEEN))));
  map->insert(std::make_pair("BY", Token_ptr(new Token(&Token::BY))));
  map->insert(std::make_pair("CASE", Token_ptr(new Token(&Token::CASE))));
  map->insert(std::make_pair("CAST", Token_ptr(new Token(&Token::CAST))));
  map->insert(std::make_pair("CHECK", Token_ptr(new Token(&Token::CHECK))));
  map->insert(std::make_pair("CONSTRAINT", Token_ptr(new Token(&Token::CONSTRAINT))));
  map->insert(std::make_pair("CREATE", Token_ptr(new Token(&Token::CREATE))));
  map->insert(std::make_pair("DATABASE", Token_ptr(new Token(&Token::DATABASE))));
  map->insert(std::make_pair("DEFAULT", Token_ptr(new Token(&Token::DEFAULT))));
  map->insert(std::make_pair("COLUMN", Token_ptr(new Token(&Token::COLUMN))));
  map->insert(std::make_pair("TABLESPACE", Token_ptr(new Token(&Token::TABLESPACE))));
  map->insert(std::make_pair("PROCEDURE", Token_ptr(new Token(&Token::PROCEDURE))));
  map->insert(std::make_pair("FUNCTION", Token_ptr(new Token(&Token::FUNCTION))));
  map->insert(std::make_pair("DELETE", Token_ptr(new Token(&Token::DELETE))));
  map->insert(std::make_pair("DESC", Token_ptr(new Token(&Token::DESC))));
  map->insert(std::make_pair("DISTINCT", Token_ptr(new Token(&Token::DISTINCT))));
  map->insert(std::make_pair("DROP", Token_ptr(new Token(&Token::DROP))));
  map->insert(std::make_pair("ELSE", Token_ptr(new Token(&Token::ELSE))));
  map->insert(std::make_pair("EXPLAIN", Token_ptr(new Token(&Token::EXPLAIN))));
  map->insert(std::make_pair("EXCEPT", Token_ptr(new Token(&Token::EXCEPT))));
  map->insert(std::make_pair("END", Token_ptr(new Token(&Token::END))));
  map->insert(std::make_pair("ESCAPE", Token_ptr(new Token(&Token::ESCAPE))));
  map->insert(std::make_pair("EXISTS", Token_ptr(new Token(&Token::EXISTS))));
  map->insert(std::make_pair("FOR", Token_ptr(new Token(&Token::FOR))));
  map->insert(std::make_pair("FOREIGN", Token_ptr(new Token(&Token::FOREIGN))));
  map->insert(std::make_pair("FROM", Token_ptr(new Token(&Token::FROM))));
  map->insert(std::make_pair("FULL", Token_ptr(new Token(&Token::FULL))));
  map->insert(std::make_pair("GROUP", Token_ptr(new Token(&Token::GROUP))));
  map->insert(std::make_pair("HAVING", Token_ptr(new Token(&Token::HAVING))));
  map->insert(std::make_pair("IN", Token_ptr(new Token(&Token::IN))));
  map->insert(std::make_pair("INDEX", Token_ptr(new Token(&Token::INDEX))));
  map->insert(std::make_pair("INNER", Token_ptr(new Token(&Token::INNER))));
  map->insert(std::make_pair("INSERT", Token_ptr(new Token(&Token::INSERT))));
  map->insert(std::make_pair("INTERSECT", Token_ptr(new Token(&Token::INTERSECT))));
  map->insert(std::make_pair("INTERVAL", Token_ptr(new Token(&Token::INTERVAL))));
  map->insert(std::make_pair("INTO", Token_ptr(new Token(&Token::INTO))));
  map->insert(std::make_pair("IS", Token_ptr(new Token(&Token::IS))));
  map->insert(std::make_pair("JOIN", Token_ptr(new Token(&Token::JOIN))));
  map->insert(std::make_pair("KEY", Token_ptr(new Token(&Token::KEY))));
  map->insert(std::make_pair("LEFT", Token_ptr(new Token(&Token::LEFT))));
  map->insert(std::make_pair("LIKE", Token_ptr(new Token(&Token::LIKE))));
  map->insert(std::make_pair("LOCK", Token_ptr(new Token(&Token::LOCK))));
  map->insert(std::make_pair("MINUS", Token_ptr(new Token(&Token::MINUS))));
  map->insert(std::make_pair("NOT", Token_ptr(new Token(&Token::NOT))));
  map->insert(std::make_pair("NULL", Token_ptr(new Token(&Token::XNULL))));
  map->insert(std::make_pair("ON", Token_ptr(new Token(&Token::ON))));
  map->insert(std::make_pair("OR", Token_ptr(new Token(&Token::OR))));
  map->insert(std::make_pair("ORDER", Token_ptr(new Token(&Token::ORDER))));
  map->insert(std::make_pair("OUTER", Token_ptr(new Token(&Token::OUTER))));
  map->insert(std::make_pair("PRIMARY", Token_ptr(new Token(&Token::PRIMARY))));
  map->insert(std::make_pair("REFERENCES", Token_ptr(new Token(&Token::REFERENCES))));
  map->insert(std::make_pair("RIGHT", Token_ptr(new Token(&Token::RIGHT))));
  map->insert(std::make_pair("SCHEMA", Token_ptr(new Token(&Token::SCHEMA))));
  map->insert(std::make_pair("SELECT", Token_ptr(new Token(&Token::SELECT))));
  map->insert(std::make_pair("SET", Token_ptr(new Token(&Token::SET))));
  map->insert(std::make_pair("SOME", Token_ptr(new Token(&Token::SOME))));
  map->insert(std::make_pair("TABLE", Token_ptr(new Token(&Token::TABLE))));
  map->insert(std::make_pair("THEN", Token_ptr(new Token(&Token::THEN))));
  map->insert(std::make_pair("TRUNCATE", Token_ptr(new Token(&Token::TRUNCATE))));
  map->insert(std::make_pair("UNION", Token_ptr(new Token(&Token::UNION))));
  map->insert(std::make_pair("UNIQUE", Token_ptr(new Token(&Token::UNIQUE))));
  map->insert(std::make_pair("UPDATE", Token_ptr(new Token(&Token::UPDATE))));
  map->insert(std::make_pair("VALUES", Token_ptr(new Token(&Token::VALUES))));
  map->insert(std::make_pair("VIEW", Token_ptr(new Token(&Token::VIEW))));
  map->insert(std::make_pair("SEQUENCE", Token_ptr(new Token(&Token::SEQUENCE))));
  map->insert(std::make_pair("TRIGGER", Token_ptr(new Token(&Token::TRIGGER))));
  map->insert(std::make_pair("USER", Token_ptr(new Token(&Token::USER))));
  map->insert(std::make_pair("WHEN", Token_ptr(new Token(&Token::WHEN))));
  map->insert(std::make_pair("WHERE", Token_ptr(new Token(&Token::WHERE))));
  map->insert(std::make_pair("XOR", Token_ptr(new Token(&Token::XOR))));
  map->insert(std::make_pair("OVER", Token_ptr(new Token(&Token::OVER))));
  map->insert(std::make_pair("TO", Token_ptr(new Token(&Token::TO))));
  map->insert(std::make_pair("USE", Token_ptr(new Token(&Token::USE))));
  map->insert(std::make_pair("REPLACE", Token_ptr(new Token(&Token::REPLACE))));
  map->insert(std::make_pair("COMMENT", Token_ptr(new Token(&Token::COMMENT))));
  map->insert(std::make_pair("COMPUTE", Token_ptr(new Token(&Token::COMPUTE))));
  map->insert(std::make_pair("WITH", Token_ptr(new Token(&Token::WITH))));
  map->insert(std::make_pair("GRANT", Token_ptr(new Token(&Token::GRANT))));
  map->insert(std::make_pair("REVOKE", Token_ptr(new Token(&Token::REVOKE))));
  map->insert(std::make_pair("WHILE", Token_ptr(new Token(&Token::WHILE))));
  map->insert(std::make_pair("DO", Token_ptr(new Token(&Token::DO))));
  map->insert(std::make_pair("DECLARE", Token_ptr(new Token(&Token::DECLARE))));
  map->insert(std::make_pair("LOOP", Token_ptr(new Token(&Token::LOOP))));
  map->insert(std::make_pair("LEAVE", Token_ptr(new Token(&Token::LEAVE))));
  map->insert(std::make_pair("ITERATE", Token_ptr(new Token(&Token::ITERATE))));
  map->insert(std::make_pair("REPEAT", Token_ptr(new Token(&Token::REPEAT))));
  map->insert(std::make_pair("UNTIL", Token_ptr(new Token(&Token::UNTIL))));
  map->insert(std::make_pair("OPEN", Token_ptr(new Token(&Token::OPEN))));
  map->insert(std::make_pair("CLOSE", Token_ptr(new Token(&Token::CLOSE))));
  map->insert(std::make_pair("CURSOR", Token_ptr(new Token(&Token::CURSOR))));
  map->insert(std::make_pair("FETCH", Token_ptr(new Token(&Token::FETCH))));
  map->insert(std::make_pair("OUT", Token_ptr(new Token(&Token::OUT))));
  map->insert(std::make_pair("INOUT", Token_ptr(new Token(&Token::INOUT))));
  map->insert(std::make_pair("LIMIT", Token_ptr(new Token(&Token::LIMIT))));

  DEFAULT_KEYWORDS = Keywords_ptr(new Keywords(map));

  {
    // Map<String, Token> sqlitemap = new HashMap<String, Token>();
    std::shared_ptr<std::unordered_map<std::string, Token_ptr>> sqlitemap = std::make_shared<std::unordered_map<std::string, Token_ptr>>();

    // sqlitemap.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());
    for (auto it : *DEFAULT_KEYWORDS->getKeywords())
    {
      sqlitemap->insert(std::make_pair(it.first, it.second));
    }

    sqlitemap->insert(std::make_pair("LIMIT", Token_ptr(new Token(&Token::LIMIT))));
    SQLITE_KEYWORDS = Keywords_ptr(new Keywords(sqlitemap));
  }

  {
    // Map<String, Token> sqlitemap = new HashMap<String, Token>();
    std::shared_ptr<std::unordered_map<std::string, Token_ptr>> sqlitemap = std::make_shared<std::unordered_map<std::string, Token_ptr>>();

    // sqlitemap.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());
    for (auto it : *DEFAULT_KEYWORDS->getKeywords())
    {
      sqlitemap->insert(std::make_pair(it.first, it.second));
    }

    sqlitemap->insert(std::make_pair("MERGE", Token_ptr(new Token(&Token::MERGE))));
    sqlitemap->insert(std::make_pair("MATCHED", Token_ptr(new Token(&Token::MATCHED))));
    sqlitemap->insert(std::make_pair("USING", Token_ptr(new Token(&Token::USING))));
    DM_KEYWORDS = Keywords_ptr(new Keywords(sqlitemap));
  }
}

bool Keywords::containsValue(Token_ptr token)
{
  // return this->keywords.containsValue(token);
  for (auto it : *this->keywords)
  {
    if (it.second->name->c_str() == token->name->c_str())
    {
      return true;
    }
  }
  return false;
}

Keywords::Keywords(std::shared_ptr<std::unordered_map<std::string, Token_ptr>> keywords)
{
  this->keywords = keywords;

  // this->hashArray = new long[keywords.size()];
  // this->tokens = new Token[keywords.size()];

  int index = 0;
  for (auto k : *keywords)
  {
    // LOG_INFO <<"k.first:"<<k.first;
    // hashArray[index++] = FnvHash::fnv1a_64_lower(k.first);
    hashArray.emplace_back(FnvHash::fnv1a_64_lower(k.first));
    index++;
    this->tokens.emplace_back(nullptr);
  }
  // Arrays.sort(hashArray);
  std::sort(hashArray.begin(), hashArray.end());

  for (auto entry : *keywords)
  {
    long k = FnvHash::fnv1a_64_lower(entry.first);
    // index = Arrays.binarySearch(hashArray, k);
    int index = -1;
    int size = hashArray.size();
    for (int i = 0; i < size; i++)
    {
      // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
      if (hashArray[i] == k)
      {
        index = i;
        break;
      }
    }

    tokens[index] = entry.second;
  }
}

Token_ptr Keywords::getKeyword(long hash)
{
  // int index = Arrays.binarySearch(hashArray, hash);
  int index = -1;
  int size = hashArray.size();
  for (int i = 0; i < size; i++)
  {
    // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
    if (hashArray[i] == hash)
    {
      index = i;
      break;
    }
  }
  LOG_INFO <<  "index:" << index;
  if (index < 0)
  {
    return nullptr;
  }
  return tokens[index];
}

Token_ptr Keywords::getKeyword(std::string key)
{
  long k = FnvHash::fnv1a_64_lower(key);
  // int index = Arrays.binarySearch(hashArray, k);
  int index = -1;
  int size = hashArray.size();
  for (int i = 0; i < size; i++)
  {
    // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
    if (hashArray[i] == k)
    {
      index = i;
      break;
    }
  }

  if (index < 0)
  {
    return nullptr;
  }
  return tokens[index];
  //        return keywords.get(key);
}

std::shared_ptr<std::unordered_map<std::string, Token_ptr>> Keywords::getKeywords()
{
  return keywords;
}
